﻿#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;

namespace FT.Architecture.Utils.ExtensionMethods
{
    /// <summary>
    /// Extensions Methods for Dictionaries
    /// </summary>
    public static class DictionaryExtensions
    {
        /// <summary>
        /// Converts a Dictionary of type T,S to type R,Q
        /// </summary>
        /// <typeparam name="TKeySource">The source key type</typeparam>
        /// <typeparam name="TValueSource">The source value type</typeparam>
        /// <typeparam name="TKeyDestination">The destination key type</typeparam>
        /// <typeparam name="TSourceDestination">The destination value type</typeparam>
        /// <param name="originalDictionary"></param>
        /// <returns></returns>
        public static IDictionary<TKeyDestination, TSourceDestination> ConvertDictionary
            <TKeySource, TValueSource, TKeyDestination, TSourceDestination>
            (this IDictionary<TKeySource, TValueSource> originalDictionary)
            where TKeySource : TKeyDestination
            where TValueSource : TSourceDestination
        {
            IDictionary<TKeyDestination, TSourceDestination> newDictionary =
                new Dictionary<TKeyDestination, TSourceDestination>();

            foreach (KeyValuePair<TKeySource, TValueSource> pair in originalDictionary)
                newDictionary.Add(pair.Key, pair.Value);

            return newDictionary;
        }

        /// <summary>
        /// Invert the Key-Values in a dictionary so the values become the keys and vice versa
        /// The mapping must be 1:1.  If there are multiple keys with the same values, only the first
        /// value will be used (in the order that the keys are iterated)
        /// </summary>
        /// <typeparam name="TOne"></typeparam>
        /// <typeparam name="TTwo"></typeparam>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        public static IDictionary<TTwo, TOne> Invert<TOne, TTwo>(this IDictionary<TOne, TTwo> dictionary)
        {
            IDictionary<TTwo, TOne> invertedDictionary = new Dictionary<TTwo, TOne>();

            foreach (KeyValuePair<TOne, TTwo> pair in dictionary)
            {
                if (!invertedDictionary.ContainsKey(pair.Value))
                    invertedDictionary[pair.Value] = pair.Key;
            }

            return invertedDictionary;
        }

        /// <summary>
        /// Invert the Key-Values in a dictionary so the values become the keys and vice versa
        /// If there are multiple keys with the same values,All the keys will be added as a list
        /// </summary>
        /// <typeparam name="TOne"></typeparam>
        /// <typeparam name="TTwo"></typeparam>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        public static IDictionary<TTwo, IEnumerable<TOne>> InvertAndRetainAll<TOne, TTwo>(this IDictionary<TOne, TTwo> dictionary)
        {
            IDictionary<TTwo, IEnumerable<TOne>> invertedDictionary = new Dictionary<TTwo, IEnumerable<TOne>>();

            foreach (KeyValuePair<TOne, TTwo> pair in dictionary)
            {
                if (!invertedDictionary.ContainsKey(pair.Value))
                    invertedDictionary[pair.Value] = new List<TOne>();

                ((IList<TOne>) invertedDictionary[pair.Value]).Add(pair.Key);
            }

            return invertedDictionary;
        }

        /// <summary>
        /// Delegate
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public delegate T ToEnumerableDelegate<TKey, TValue, T>(TKey key, TValue value);

        /// <summary>
        /// Transform a dictionary into an enumerable, using a delegate to transform a key and a value into a single value
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="dictionary"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IEnumerable<T> ToEnumerable<TKey, TValue, T>(this IDictionary<TKey, TValue> dictionary,
                                                           ToEnumerableDelegate<TKey, TValue, T> func)
        {
            var results = new List<T>();
            foreach (KeyValuePair<TKey, TValue> keyValuePair in dictionary)
            {
                results.Add(func(keyValuePair.Key, keyValuePair.Value));
            }

            return results;
        }
    }
}